4 // Copyright (c) 2006 Microsoft Corporation. All rights reserved.
6 // The use and distribution terms for this software are contained in the file
7 // named license.txt, which can be found in the root of this distribution.
8 // By using this software in any fashion, you are agreeing to be bound by the
9 // terms of this license.
11 // You must not remove this notice, or any other, from this software.
15 // RPN.C -- expression evaluator
18 // This module contains NMAKE's expression evaluator routines.
27 char * GetEndQuote(void);
28 char * GetEndBracket(void);
29 void check_syntax_error(UCHAR
);
30 void type_and_val(UCHAR
, INT_PTR
);
31 void pushIntoList(void);
33 BOOL
handleExpr(void);
34 BOOL
handleExists(char*);
35 BOOL
handleDefines(char*);
37 BOOL
do_binary_op(UCHAR
);
38 BOOL
do_unary_op(UCHAR
);
39 UCHAR
match(char *tokPtr
);
40 void chkInvocAndPush(RPNINFO
*pListPtr
);
42 #define TEMPSTACKSIZE 512 // size of temporary stack
43 #define LISTSIZE 1024 // size of list of rpn-form items
45 RPNINFO tempStack
[TEMPSTACKSIZE
]; // temporary/operand stack
46 RPNINFO rpnList
[LISTSIZE
]; // list of items in rpn order
47 char * text
; // pointer to expr text in lbufPtr
48 UCHAR prevTok
; // initial token put on tempstack
49 BOOL done
; // true if there are no more tokens
50 UCHAR errRow
; // first token is '(' so error table
51 // row val is 3. See check_syntax....
52 RPNINFO
* pTop
; // top item on tempStack
53 RPNINFO
* pList
; // next free slot in list
54 RPNINFO
* pEnd
= &(tempStack
[TEMPSTACKSIZE
-1]);
55 RPNINFO
* pListEnd
= &(rpnList
[LISTSIZE
-1]);
59 // do_binary_op() - do operation on two stack operands
61 // arguments: type - operator type code
63 // actions : pops first operand from the stack (tempStack).
64 // checks the types of the two operands (the operand
65 // that was popped as well as the operand currently
66 // on top of the stack).
67 // if both operands are integers then do the operation
68 // else if both operands are strings and operation is
69 // the equality operation then do it.
70 // else return FALSE ( illegal operation )
72 // modifies : tempStack - top element will now be the result of
84 pOldTop
= pTop
--; // pop one item off stack, with a ptr to it
85 right
= &pOldTop
->valPtr
;
88 if ((pOldTop
->type
== INTEGER
) && (pTop
->type
== INTEGER
)) {
91 *left
= *left
|| *right
;
95 *left
= *left
&& *right
;
111 *left
= *right
!= *left
;
115 *left
= *right
== *left
;
119 *left
= *left
> *right
;
123 *left
= *left
< *right
;
127 *left
= *left
>= *right
;
131 *left
= *left
<= *right
;
152 makeError(line
, DIVIDE_BY_ZERO
);
158 makeError(line
, DIVIDE_BY_ZERO
);
170 } else if ((pOldTop
->type
== STR
) && (pTop
->type
== STR
)) {
171 int i
= _tcscmp((char *) *left
, (char *) *right
);
203 pTop
->type
= INTEGER
;
212 // do_unary_op() - do operation on top stack operand
214 // arguments: type - operator type code
216 // actions : checks the type of the top operand on the stack
217 // if operand is an integer then do the operation
218 // else return FALSE ( illegal operation )
220 // modifies : tempStack - top element will now be the result of
232 if (pTop
->type
== INTEGER
) {
261 // Return the pointer to the next double-quote character in text. A
262 // double-quote followed immediately by a double-quote is skipped.
264 // text : the global ptr to the buffer is moved up beyond this string.
271 for (pStart
= ++text
; *text
; ++text
)
280 makeError(line
, SYNTAX_MISSING_END_CHAR
, '\"');
282 *text
++ = '\0'; // null byte over closing quote
289 // Lexes a program invocation.
291 // Program invocation is of the form: [ prog <arglist> ].
292 // Process escaped ']' here because this is where we do the lexing.
294 // text : the global ptr to the buffer is moved up beyond this string.
301 for (pStart
= ++text
; *text
; text
= _tcsinc (text
)) {
302 if (*text
== ESCH
&& text
[1] == ']')
303 memmove(text
, text
+ 1, 1 + _tcslen(text
+ 1));
304 else if (*text
== ']')
309 makeError(line
, SYNTAX_MISSING_END_CHAR
, ']');
311 *text
++ = '\0'; // null byte over closing bracket
316 // check_syntax_error() - check if there is a syntax error in expr
318 // arguments: type - type of the current token
320 // actions: checks the type of the current token against the type
321 // of the previous token.
326 // alpha op unary_op ( )
327 // ------------------------------------------------
328 // alpha | 0 | 1 | 0 | 0 | 1 |
329 // -------------------------------------------------
330 // op | 1 | 0 | 1 | 1 | 0 |
331 // -------------------------------------------------
332 // unary_op | 1 | 0 | 0 | 1 | 0 |
333 // -------------------------------------------------
334 // ( | 1 | 0 | 1 | 1 | 0 |
335 // -------------------------------------------------
336 // ) | 0 | 1 | 0 | 0 | 1 |
337 // -------------------------------------------------
340 // alpha : a primary ( integer, str, prog. invoc. )
341 // op : a binary operator
342 // unary_op : a unary operator ( ~, !, - ). A ZERO in the slot => error
344 // NOTE: ANY CHANGES TO THE TYPE VALUES WILL AFFECT THIS ROUTINE.
351 // extern UCHAR errRow;
354 if (newTok
== LEFT_PAREN
)
356 else if (newTok
== RIGHT_PAREN
)
358 else if (newTok
> LOGICAL_NOT
)
360 else if (newTok
> MULTIPLY
)
365 if (!errTable
[errRow
][errCol
])
366 makeError(line
, SYNTAX_INVALID_EXPR
);
367 errRow
= errCol
; // this becomes the first token the next time
373 // arguments: type - the type code of the present operator.
374 // val - ptr to a str/or integer
376 // initialises a record with the type code, after checking for any
377 // syntax errors. The new token is checked against the previous token
378 // for illegal combinations of tokens.
379 // initialises the record with the integer value/string ptr.
387 // extern RPNINFO tokRec; // returned to handleExpr
388 // extern UCHAR prevTok; // token last seen
390 check_syntax_error(type
);
399 // arguments: tokPtr - ptr to a token string ( in tokTable )
401 // actions : looks for a substring in the expression buffer
402 // pointed to by 'text', that matches the given token.
403 // if substring found, returns TRUE, else returns FALSE.
410 // extern char *text;
413 while (*tokPtr
&& (*t
== *tokPtr
)) {
429 // gets a token from the expression buffer.
430 // if the current char from the buffer is a space/tab, skip space/tabs
431 // until we get a non-space char ( could be NULL char ).
432 // Check if we are now at the beginning of one of the tokens in the
433 // tokenTable. This covers most tokens.
434 // Check if we have a minus. If a minus and the previous token was an
435 // integer, this is a binary minus, else a unary minus.
436 // If the current char is a double-quote, we are at the start of a
438 // If the current char is a '[', we are at the start of a program
439 // invocation. In both cases, the escape character is '\\'.
440 // If current char is a digit, we have a constant ( integer ).
441 // Else we have defined(ID).
442 // If none of the above, if current char is NULL, break out, else
443 // report error ( illegal character string has been found ).
445 // If we came to the NULL char at the end of the buffer, set global
446 // flag 'done' to TRUE, return a RIGHT_PAREN to match the opening
450 // modifies: text : ptr to expression buffer.
451 // prevTok: thru' calls to type_and_val().
452 // done : at end of buffer
453 // errRow : index into error table, thru calls to
455 // returns : token in tokRec(global, static to the module). The
456 // token has the new type/integer/ptr values.
461 // extern UCHAR prevTok;
469 if (c
== ' ' || c
== '\t') {
471 c
= *++text
; // skip white spaces
474 if (IS_OPERATORCHAR(c
)) {
475 for (p
= tokTable
; p
->op_str
&& !match(p
->op_str
); p
++)
478 // make p point to last entry in table
479 p
= &tokTable
[(sizeof(tokTable
) / sizeof(TOKTABREC
)) - 1];
483 type_and_val(p
->op
, 0);
485 if (c
== '-') { // now check if binary or unary minus to be returned
487 if (prevTok
== INTEGER
)
488 type_and_val(BINARY_MINUS
, 0);
490 type_and_val(UNARY_MINUS
, 0);
493 type_and_val(STR
, (INT_PTR
) GetEndQuote());
496 type_and_val(PROG_INVOC_STR
, (INT_PTR
) GetEndBracket());
497 } else { // integers and IDs handled here
499 char *pNumber
= text
;
501 errno
= 0; // Accept decimal, octal or hex no
502 constant
= strtol(text
, &text
, 0);
503 if (errno
== ERANGE
) {
505 makeError(line
, CONST_TOO_BIG
, pNumber
);
508 if (_totupper(*text
) == 'L')
510 type_and_val(INTEGER
, constant
);
511 } else { // defined(ID) comes here
513 if (!_tcsnicmp(text
, "DEFINED", 7)) {
514 if (!(ptr
= _tcschr(text
, '(')))
515 makeError(line
, SYNTAX_INVALID_EXPR
);
517 text
= ptr
+ _tcscspn(ptr
, ")");
519 type_and_val(INTEGER
, handleDefines(ptr
));
521 else if (!_tcsnicmp(text
, "EXIST", 5)) {
522 if (!(ptr
= _tcschr(text
, '(')))
523 makeError(line
, SYNTAX_INVALID_EXPR
);
525 text
= ptr
+ _tcscspn(ptr
, ")");
527 type_and_val(INTEGER
, handleExists(ptr
));
530 makeError(line
, SYNTAX_INVALID_EXPR
);
531 } else { // we are now at the end of the string (c is null)
533 type_and_val(RIGHT_PAREN
, 0); // this is the last token
540 // chkInvocAndPush() - check if program invocation required
542 // arguments: pListPtr - might have a program invocation string
545 // actions : if this is a program invocation string, make the
546 // program invocation.
547 // the return value is got and placed on the stack.
548 // the type of the new stack element is now INTEGER.
549 // else place list item on stack.
551 // in either case it moves one item from list to stack.
559 if (pListPtr
->type
== PROG_INVOC_STR
) {
560 pTop
->valPtr
= execLine((char *) pListPtr
->valPtr
, FALSE
, TRUE
, FALSE
, NULL
);
561 pTop
->type
= INTEGER
;
572 // actions : remove an item from the list.
573 // if the item is an operand, place it on the operand
574 // stack (tempStack).
575 // if the operand is a program invocation string, make
576 // the invocation, place the return code on stack.
577 // if the item is an operator, call the function to
578 // do the operation on one/two elements on tempStack.
580 // finally, check if there is exactly one item on stack.
581 // if this item has a value of zero, return FALSE.
583 // if more than one item on stack, abort with error.
585 // modifies: pTop - ptr to top of tempStack.
586 // pList - ptr to next position in list.
591 // extern RPNINFO *pList;
592 // extern RPNINFO *pTop;
594 BOOL (* func
)(UCHAR
);
596 for (pTemp
= rpnList
; pTemp
< pList
; pTemp
++) {
597 if (pTemp
->type
> LOGICAL_NOT
) { // operand
598 chkInvocAndPush(pTemp
);
600 if (pTemp
->type
> MULTIPLY
)
605 if (!(*func
)(pTemp
->type
))
606 makeError(line
, BAD_OP_TYPES
);
610 if ((pTop
== tempStack
) && (pTop
->type
== INTEGER
))
616 makeError(line
, SYNTAX_INVALID_EXPR
);
626 // actions : pops an item from the tempStack and pushes it onto
627 // the list. checks list for overflow ( internal error )
628 // and tempStack for underflow ( syntax error in expr ).
630 // modifies: tempTop - index of top of tempStack.
631 // nextInList - index to next position in list.
636 if (pTop
< tempStack
)
637 makeError(line
, SYNTAX_INVALID_EXPR
);
639 if (pList
> pListEnd
)
640 makeError(line
, EXPR_TOO_LONG_INTERNAL
);
643 // Keep track of the high water mark on the stack just for grins
645 static int iStackMax
= 0;
646 if ( pList
- rpnList
> iStackMax
)
647 iStackMax
= (int) (pList
- rpnList
);
657 // arguments: text - pointer to the buffer that has the expression.
659 // actions : calls getTok() to get tokens from the buffer. Places
660 // tokens in a tempStack, and moves them into a list in
661 // reverse-polish order.
663 // We need the list so that ALL syntax errors are caught
664 // BEFORE processing of the expression begins (especially
665 // program invocations that have side effects)
667 // Once the list is available, an operand stack is used
668 // Items are popped and pushed from this stack by the
669 // evaluation routines (add, mult, negate etc.)
671 // we don't really need a separate operand stack. the
672 // tempStack has served its purpose when the list is
673 // formed and so it may be used for operand processing.
678 // extern RPNINFO tokRec;
679 BOOL fRParen
; // was the token got a right paren?
681 // extern RPNINFO *pTop, *pList;
682 // extern UCHAR errRow;
683 // extern UCHAR prevTok;
688 errRow
= 3; // row for the first token put in,left paren
689 prevTok
= LEFT_PAREN
;
690 type_and_val(LEFT_PAREN
, 0);
693 while (!done
) { // while there are more tokens in buffer
696 if (tokRec
.type
!= LEFT_PAREN
) {
697 while (precVector
[tokRec
.type
] <= precVector
[pTop
->type
]) {
698 if (!precVector
[tokRec
.type
]) { // if RIGHT_PAREN pop till a
699 // left paren is seen
700 while (pTop
->type
!= LEFT_PAREN
)
703 if (pTop
< tempStack
) {
704 makeError(line
, SYNTAX_INVALID_EXPR
);
706 pTop
--; // pop the left paren
714 // if token is a left paren, it has to go on the stack
717 makeError(line
, EXPR_TOO_LONG_INTERNAL
);
723 // check the stack here for not empty state
724 if (pTop
!= tempStack
- 1)
725 makeError(line
, SYNTAX_INVALID_EXPR
);
726 return(processList());
732 // arguments: t pointer to buffer that has the identifier
734 // actions: Checks if one of 'ID' is present.
735 // Aborts with error if more IDs present.
736 // Is called for ifdef/ifndef/defined(ID).
738 // returns : TRUE if ID found in table. FALSE otherwise.
747 s
= _tcstok(t
, " \t");
748 if (_tcstok(NULL
, " \t")) {
749 makeError(line
, SYNTAX_UNEXPECTED_TOKEN
, s
);
753 makeError(line
, MISSING_ARG_BEFORE_PAREN
);
766 // arguments: t pointer to buffer that has the identifier
768 // actions: Checks if 'name' is a valid file/directory
769 // Aborts with error if more names present.
770 // Is called for exist(name).
772 // returns : TRUE if ID found in table. FALSE otherwise.
780 char *szUnQuoted
= NULL
;
781 BOOL fResult
= FALSE
;
785 // make local copy, strip blank space before and after string
786 char *tSav
= t
= makeString(_t
);
787 while (*t
&& WHITESPACE (*t
)) {
793 if (WHITESPACE (*s
)) {
801 szDelim
= ('\"' == *t
) ? (char *)"\t" : (char *)" \t";
802 // if id starts with a quote,
803 // use "\t" instead of " \t" in _tcstok
804 // (handle paths with embedded spaces)
805 s
= _tcstok(t
, szDelim
);
806 if (_tcstok(NULL
, szDelim
)) {
807 makeError(line
, SYNTAX_UNEXPECTED_TOKEN
, s
);
810 if (NULL
== s
|| NULL
== (szUnQuoted
= unQuote(s
))) { // handle quoted names
811 makeError(line
, MISSING_ARG_BEFORE_PAREN
);
814 if (!_access(szUnQuoted
, 0x00)) { // existence check
827 // arguments: t pointer to buffer that has the expression
828 // kind specifies if it is if/ifdef/ifndef etc.
830 // returns : TRUE if expression evaluates to true.
840 makeError(line
, SYNTAX_MISSING_DIRECTIVE
);
845 case ELSE_IFDEF_TYPE
:
846 return(handleDefines(t
));
849 case ELSE_IFNDEF_TYPE
:
850 return((BOOL
)!handleDefines(t
));
854 return(handleExpr());